#include <std.h>
#include <system.h>
#include <pio.h>
#include <error.h>
#include <pipe.h> 
#include <type.h>
static struct processstate *head;
static unsigned long glob_id;

#define AWAKE 0
#define SLEEP 1
#define TEMP_SLEEP 2
#define DET_SLEEP 3

void init_sched();

/*Following two functions are only used in asm in irq0 interrupt. These are not supposed
to be used anywhere else*/
extern void save_state_immediate();
extern void load_state_immediate();

void show_state(CPU_state *);
void scheduler();

/*Following two functions load process states into temporary system states that will be loaded
at the end of irq0*/
int load_process(CPU_state *);
int save_process(CPU_state *);
int set_stack(CPU_state *state,void *entry,unsigned long stack); //This system function sets a proper stack of the process

static unsigned long t;

void init_process(struct processstate *state)
{
	int i;
	init_cpu_state(&state->cpustate);
	set_stack(&state->cpustate,state->entry_fn,state->stack);
	t=state->tmout;
	state->sleep_state=AWAKE;
	state->idle_timer=NULL;
	for(i=0;i<MAXPIPES;i++)	{
		state->pipehandle[i]=NULL;
	}
}

/*To protect below function from corruption we can't use spinlocks. If the function is interrupted before
the completion of the update of the process list then the scheduler might end up in
running an incomplete process entry which might lead to faults*/

void add_process(char *name,void *entry,unsigned long stack)
{
	struct processstate *new;
	start_mono();
	add_node_end(struct processstate,head,new);
	new->tmout=1;
	new->entry_fn=entry;
	new->stack=stack;
	new->process_id=++glob_id;
	strcpy(new->process_name,name);
	init_process(new);
	printf("\nProcess initiated:%x:%s",new,new->process_name);
	release_mono();
}

#define increase_timeslice(tstate0)	({ \
	tstate0->tmout++; \
})

#define decrease_timeslice(tstate0)	({ \
	if((tstate0->tmout)>0) \
		tstate0->tmout--; \
})

/* This function does not modify the process list. Hence a spinlock.*/
unsigned long get_process_id(char *name)
{
	struct processstate *temp;
	spin_lock gid;
	acquire_spin_lock(&gid);
	for(temp=head;(strcmp(temp->process_name,name)!=NULL) && temp!=NULL;temp=temp->next);
	release_spin_lock(&gid);
	return temp->process_id;
}

int delete_process(unsigned long id)
{
	struct processstate *temp;
	start_mono();
	get_node(struct processstate,head,process_id,id,temp);
	delete_node(struct processstate,head,temp);
	printf("\nProcess ended:%s",temp->process_name);
	release_mono();
	return NULL;
}

unsigned long running_process_id();

/*For this function we first need to check whether the caller is head*/
int end_all_processes()
{
	struct processstate *temp;
	if((running_process_id())!=1)	
		return -1;
	for(temp=head->next;temp!=NULL;temp=temp->next)	{
		delete_node(struct processstate,head,temp);
		printf("\nProcess ended:%s",temp->process_name);
	}
}

static struct processstate *running;
int fsched=0;
void switchnext()
{
	fsched=0;
	if(running!=NULL)	{
		save_process(&running->cpustate);
		running=running->next;
		while((running->sleep_state)!=AWAKE && running!=NULL)	{
			if((running->sleep_state)==TEMP_SLEEP)
				running->sleep_state=AWAKE;
			if((running->sleep_state)==DET_SLEEP)	{
				if(--(running->sleep_count)<=0)
					running->sleep_state=AWAKE;
			}				
			running=running->next;
		}
		if(running==NULL)	{
			running=head;
		}
	}
	else	{
		running=head;
	}
	if(running==NULL)	{
		i_error("\nNo process");
	}
	load_process(&running->cpustate);
	t=running->tmout;
}

void sleep_process(unsigned long id)
{
	struct processstate *tmp;
	get_node(struct processstate,head,process_id,id,tmp);
	tmp->sleep_state=SLEEP;
	tmp->sleep_count=0;
}

void wake_process(unsigned long id)
{
	struct processstate *tmp;
	get_node(struct processstate,head,process_id,id,tmp);
	tmp->sleep_state=AWAKE;
	tmp->sleep_count=0;
}

void temp_sleep_process(unsigned long id)
{
	struct processstate *tmp;
	get_node(struct processstate,head,process_id,id,tmp);
	tmp->sleep_state=TEMP_SLEEP;
	tmp->sleep_count=0;
}

/*Predetermined sleep period*/
void det_sleep_process(unsigned long id,int count)
{
	struct processstate *tmp;
	get_node(struct processstate,head,process_id,id,tmp);
	tmp->sleep_state=DET_SLEEP;
	tmp->sleep_count=count;
}

void dec_time(unsigned long id)
{
	struct processstate *tmp;
	get_node(struct processstate,head,process_id,id,tmp);
	if(tmp->tmout>1)
		tmp->tmout--;
}

void inc_time(unsigned long id)
{
	struct processstate *tmp;
	get_node(struct processstate,head,process_id,id,tmp);
	tmp->tmout++;
}

void default_time(unsigned long id)
{
	struct processstate *tmp;
	get_node(struct processstate,head,process_id,id,tmp);
	tmp->tmout=10;
}

void least_time(unsigned long id)
{
	struct processstate *tmp;
	get_node(struct processstate,head,process_id,id,tmp);
	tmp->tmout=1;
}

void idle_me(void *fn,int freq)
{
	struct processstate *tmp;
	tmp=running;
	add_timer(fn,freq);
	tmp->sleep_state=SLEEP;
	tmp->sleep_count=0;
	tmp->idle_timer=fn;
}
	
void wake_idle(unsigned long id)
{
	struct processstate *tmp;
	get_node(struct processstate,head,process_id,id,tmp);
	if(tmp->idle_timer!=NULL)
		delete_timer(tmp->idle_timer);
	tmp->sleep_state=AWAKE;
	tmp->sleep_count=0;
	tmp->idle_timer=NULL;
}

/*This fn makes the current process sleep.
It can only be woken up by any other process */
void sleep()
{
	running->sleep_state=SLEEP;
	running->sleep_count=0;
}

void temp_sleep()
{
	running->sleep_state=TEMP_SLEEP;
	running->sleep_count=0;
}

void det_sleep(int count)
{
	running->sleep_state=DET_SLEEP;
	running->sleep_count=count;
}

unsigned long running_process_id()
{
	return running->process_id;
}

CPU_state *currentprocess()
{
	return (&running->cpustate);
}

char *running_process_name()
{
	return running->process_name;
}

int scheduler_up()
{
	if(head!=NULL)	{
		return 1;
	}
	return NULL;
}

static u64 sched_time;

void scheduler()
{
	sched_time++;
	if(t>0)	{
		t--;
		if(t==NULL)	{
			switchnext();
		}
	}	
}

void get_sched_time(u64 *t)
{
	*t=sched_time; 
}

int add_pipe_process(struct pipe *p)
{
	spin_lock pipeh;
	int i;
	struct processstate *temp;
	acquire_spin_lock(&pipeh);
	temp=running;
	for(i=0;i<MAXPIPES && (temp->pipehandle[i])!=NULL;i++);
	if(i==MAXPIPES)	{
		release_spin_lock(&pipeh);
		return FAILED;
	}
	temp->pipehandle[i]=p;	
	release_spin_lock(&pipeh);
	return SUCCESS;
}

int remove_pipe_process(struct pipe *p)
{
	spin_lock pipeh;
	int i;
	struct processstate *temp;
	acquire_spin_lock(&pipeh);
	temp=running;
	for(i=0;i<MAXPIPES && (temp->pipehandle[i])!=p;i++);
	if(i==MAXPIPES)	{
		release_spin_lock(&pipeh);
		return FAILED;
	}
	temp->pipehandle[i]=NULL;
	release_spin_lock(&pipeh);
	return SUCCESS;
}

struct pipe **get_pipe(unsigned long pid)
{
	struct processstate *temp;
	get_node(struct processstate,head,process_id,pid,temp);
	return temp->pipehandle;
}


void post_sched_init();
void init_sched()
{
	printf("\nNow scheduling, ending...");
	running=NULL;
	glob_id=0;
	create_head(struct processstate,head);
	head->tmout=1;
	head->stack=0x90000;
	/*The new process uses the same stack but the first one ends as soon as the second starts..
	Hence we are safe here*/
	head->entry_fn=post_sched_init;
	strcpy(head->process_name,"salil");
	head->process_id=++glob_id;
	init_process(head);
	request_irq(0,scheduler);
	setclock(SYSTEM_SCHEDULE_CLOCK_SPEED);
	enable_irq(0);
	mdelay(100);
	i_error("Schedule failed!");
}
